package com.common.fileupload.upload;

import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.util.HexUtil;
import com.common.fileupload.domain.FileModel;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.bouncycastle.asn1.x500.style.RFC4519Style.cn;

//策略模式
public interface S3FileUploader<T> {


    String FILE_ID_KEY = "id";
    String FILE_NAME_KEY = "name";
    String FILE_SUFFIX_KEY = "suffix";
    String CONTENT_TYPE_KEY = "type";

    /**
     * 获取客户端
     */
    T getClient();



    /**
     * 上传流 java 8后接口可以定义默认方法 不能定义在类里面
     */
    default FileModel put(InputStream inputStream, String fileName, String contentType, Long fileSize) {
        return put(inputStream, null, fileName, contentType, fileSize);
    }

    FileModel put(InputStream inputStream, String prefix, String fileName, String contentType, Long fileSize);


    /**
     * 取文件信息
     */
    FileModel peek(String filePath);

    /**
     * 取文件流
     */
    InputStream get(String filePath);


    /**
     * 前缀下的列表
     */
    List<FileModel> list(String prefix);


    //上传
    default FileModel upload(MultipartFile multipart, String prefix) {
        try (final ByteArrayInputStream input = new ByteArrayInputStream(multipart.getBytes())) {
            return put(input, prefix,
                    multipart.getOriginalFilename(),
                    multipart.getContentType(),
                    multipart.getSize()
            );
        } catch (IOException e) {
            throw new IllegalArgumentException("IO错误");
        }
    }


    default FileModel upload(MultipartFile multipart) {
        return upload(multipart, null);
    }

    //批量上传
    default List<FileModel> upload(MultipartFile[] multipartFiles) {
        return Arrays.stream(multipartFiles).parallel()
                .map(this::upload)
                .collect(Collectors.toList());
    }


    //流上传
    default FileModel upload(ByteArrayInputStream input, String fileName) {
        String contentType = StringUtils.defaultIfBlank(reuseInput(input, FileTypeUtil::getType), "");
        long fileSize = reuseInput(input, this::computeFileSize);
        return put(input, fileName, contentType, fileSize);
    }


    //todo 下载
//    default void download(String filePath, HttpServletResponse response) throws Exception {
//        FileModel model = peek(filePath);
//        try (InputStream input = get(filePath);
//             BufferedInputStream bis = new BufferedInputStream(input);
//             ServletOutputStream out = response.getOutputStream()) {
//            String header = "attachment;filename=" + URLEncoder.encode(model.getOriginName(), "UTF-8");
//            response.setHeader("Content-Disposition", header);
//            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + ";charset=UTF-8");
//            byte[] bytes = new byte[1024];
////            BufferedInputStream bis = new BufferedInputStream(input);
//            int i = bis.read(bytes);
//            while (i != -1) {
//                out.write(bytes, 0, i);
//                out.flush();
//                i = bis.read(bytes);
//            }
//        }
//    }


    /**
     * 取文件大小 消耗流
     */
    default long computeFileSize(ByteArrayInputStream input) {
        byte[] buffer = new byte[1024];
        long size = 0L;
        for (int read = input.read(buffer, 0, 1024); read > -1; read = input.read(buffer, 0, 1024)) {
            size += read;
        }
        return size;
    }

    /**
     * 重用流
     */
    default <M> M reuseInput(final ByteArrayInputStream input, Function<ByteArrayInputStream, M> then) {
        //只记数组下标，不担心buffer太大，参数没用
        input.mark(0);
        return then.andThen(m -> {
            input.reset();
            return m;
        })
                .apply(input);
    }


    /**
     * 取MD5 消耗流
     */
    @Deprecated
    @SneakyThrows(IOException.class)
    default String computeMD5(InputStream inputStream) {
        //TODO 换包未测
        return DigestUtils.md5DigestAsHex(inputStream);
//        return DigestUtils.md5Hex(inputStream);
    }


    /**
     * 取大小+MD5 消耗流
     */
    @Deprecated
    @SneakyThrows(NoSuchAlgorithmException.class)
    static FileModel computeSizeAndMD5(final ByteArrayInputStream input) {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        byte[] buffer = new byte[1024];
        long size = 0L;
        for (int read = input.read(buffer, 0, 1024); read > -1; read = input.read(buffer, 0, 1024)) {
            digest.update(buffer, 0, read);
            size += read;
        }
        //TODO 换包未测
        String md5 = HexUtil.encodeHexStr(digest.digest());
//        String md5 = HexUtil.encodeHexString(digest.digest());
        return new FileModel()
//                .setMd5(md5)
                .setSize(size);
    }



}
